/* !
 * @Author: miss-tree
 * @Date: 2024-12-07 12:15:22
 * @Description: 大文件处理  
 * 暂不考虑超 2G 的文件，要做布隆过滤器处理
 * 布隆过滤器：对文件进行哈希处理，将哈希值存储在布隆过滤器中，当需要判断文件是否存在时，只需要对文件进行哈希处理，然后判断哈希值是*否在布隆过滤器中即可。
 * 只需对文件前10M和后10M进行哈希处理，然后将哈希值存储在布隆过滤器中即可。
 * 可能会存在哈希冲突，但是概率非常小。
 * 文件存储在 public/uploads 目录下
 * 临时文件存储在 public/uploads/{fileMd5Value} 目录下
*/


let express = require('express')
let formidable = require('formidable')
const router = express.Router();
let fs = require('fs-extra')
let path = require('path')
let concat = require('concat-files')
const { TryCatch } = require('../utils');
// let opn = require('opn')  // 浏览器打开文件的插件
const isDev = (process&&process.env&&process.env.NODE_ENV === 'production') || false
let uploadDir = !isDev ? '../public/uploads' : 'public/uploads'


// 检查文件的MD5 查看是否已经有文件或者已经上传部分
router.get('/check-file', (req, resp) => {
 let query = req.query
 let fileName = query.fileName
 let fileMd5Value = query.fileMd5Value
 // 获取文件Chunk列表 查看是否有文件和文件临时列表
 getChunkList(
  path.join(__dirname, uploadDir, fileName),
  path.join(__dirname, uploadDir, fileMd5Value),
  data => {
   resp.send(data)
  }
 )
})

// 获取文件Chunk列表
/**
 * @param {*} filePath 文件的路径
 * @param {*} folderPath 临时文件夹
 * @param {*} callback 
 */
async function getChunkList (filePath, folderPath, callback) {
 let isFileExit = await isExist(filePath)
 let result = {}
 // 如果文件(文件名, 如:node-v7.7.4.pkg)已在存在, 不用再继续上传, 真接秒传
 if (isFileExit) {
  result = {
   stat: 1,
   file: {
    isExist: true,
    name: filePath
   },
   desc: 'file is exist'
  }
 } else {
  // 从临时文件夹中获取已经上传的块
  let isFolderExist = await isExist(folderPath)
  // 如果文件夹(md5值后的文件)存在, 就获取已经上传的块
  let fileList = []
  if (isFolderExist) {
   fileList = await listDir(folderPath)
  }
  result = {
   stat: 1,
   chunkList: fileList,
   desc: 'folder list'
  }
 }
 callback(result)
}

// 检查chunk的MD5
router.get('/check-chunk', (req, resp) => {
 let query = req.query
 let chunkIndex = query.index
 let md5 = query.md5

 fs.stat(path.join(__dirname, uploadDir, md5, chunkIndex), (err, stats) => {
  if (stats) {
   resp.send({
    stat: 1,
    exit: true,
    desc: 'Exit 1'
   })
  } else {
   resp.send({
    stat: 1,
    exit: false,
    desc: 'Exit 0'
   })
  }
 })
})

router.get('/merge', (req, resp) => {
 let query = req.query
 let md5 = query.md5
 let size = query.size
 let fileName = query.fileName
 console.log("route merge",md5, fileName)
 mergeFiles(path.join(__dirname, uploadDir, md5), path.join(__dirname, uploadDir), fileName, size)
 resp.send({
  stat: 1
 })
})

// 若要获取body参数，当前路由不能使用当前router.use()嵌套，然后使用 upload.none() 获取
router.post('/upload-all', (req, resp) => {
 TryCatch(resp,()=>{
  var form = new formidable.IncomingForm({
   // 插件默认下载到{uploadDir} 目录 后面调用copyFile方法解决 
   uploadDir: path.resolve(__dirname, uploadDir)
  })
  form.parse(req, function (err, fields, file) {
   console.log('-upload-all--->', fields, file,"req.body",req.body)
   // fields 为上传文件信息，file 为上传的临时文件信息
  let index = fields.index ? fields.index[0] : (req.body.index||'0')
   // let total = fields.total
   let fileMd5Value = fields.index ? fields.fileMd5Value[0] : (req.body.fileMd5Value||'')
   let folder = path.resolve(__dirname, uploadDir, fileMd5Value)
   folderIsExit(folder).then(val => {
    let destFile = path.resolve(folder, index)
    let curFile = file.data?file.data[0].filepath : folder
    // console.log('-destFile--->', destFile,"curFile",curFile)
    copyFile(curFile, destFile).then(
     successLog => {
      resp.send({
       stat: true,
       desc: index
      })
     },
     errorLog => {
      resp.send({
       stat: false,
       desc: 'Error'
      })
     }
    )
   })
  })
 })

 // 文件夹是否存在, 不存在则创建文件
 function folderIsExit (folder) {
  return new Promise(async (resolve, reject) => {
   let result = await fs.ensureDirSync(path.join(folder))
   resolve(true)
  })
 }
 // 把文件从一个目录拷贝到别一个目录
 function copyFile (src, dest) {
  return new Promise((resolve, reject) => {
   fs.rename(src, dest, err => {
    if (err) {
     reject(err)
    } else {
     resolve('copy file:' + dest + ' success!')
    }
   })
  })
 }
})

// 文件或文件夹是否存在
function isExist (filePath) {
 return new Promise((resolve, reject) => {
  fs.stat(filePath, (err, stats) => {
   // 文件不存在
   if (err && err.code === 'ENOENT') {
    resolve(false)
   } else {
    resolve(true)
   }
  })
 })
}

// 列出文件夹下所有文件
function listDir (path) {
 return new Promise((resolve, reject) => {
  fs.readdir(path, (err, data) => {
   if (err) {
    reject(err)
    return
   }
   // 把mac系统下的临时文件去掉
   if (data && data.length > 0 && data[0] === '.DS_Store') {
    data.splice(0, 1)
   }
   resolve(data)
  })
 })
}
// 合并文件
/**
 * @param {*} srcDir 源文件夹
 * @param {*} targetDir 目标文件夹
 * @param {*} newFileName 新文件名
 * @param {*} size 文件大小
 */
async function mergeFiles (srcDir, targetDir, newFileName, size) {
 // console.log("mergeFiles",...arguments)
 let targetStream = fs.createWriteStream(path.join(targetDir, newFileName))
 let fileArr = await listDir(srcDir)
 fileArr.sort((x, y) => {
  return x - y;
 })
 // 把文件名加上文件夹的前缀
 for (let i = 0; i < fileArr.length; i++) {
  fileArr[i] = srcDir + '/' + fileArr[i]
 }
 let fName = [".zip", ".psd", ".pdf", ".rar", ".tar", ".gz", ".7z", ".zipx", ".tar.gz", ".tar.xz", ".tar.bz2", ".tar.lzma", ".tar.lzo", ".tar.zst", ".tar.z", ".tar.Z"]
 let filed = newFileName.split('.')
 let isZip = "." + filed[filed.length - 1]
 if (fName.includes(isZip + '22')) {
  console.log("isZip", isZip)
  // // 特殊处理 否则读取会有问题

 } else {
 }
 concat(fileArr, path.join(targetDir, newFileName), () => {
  // 删除临时文件夹
  deleteFolderRecursive(srcDir)
  console.log('Merge Success!')
 })
}

/**
 * 删除文件夹
 * @param {*} folderPath 
 */
function deleteFolderRecursive (folderPath) {
 if (fs.existsSync(folderPath)) {
  fs.readdirSync(folderPath).forEach((file, index) => {
   const curPath = path.join(folderPath, file);
   if (fs.lstatSync(curPath).isDirectory()) {
    // 递归删除子目录
    deleteFolderRecursive(curPath);
   } else {
    // 删除文件
    fs.unlinkSync(curPath);
   }
  });
  // 删除空文件夹
  fs.rmdirSync(folderPath);
 }
}


module.exports = router